package net.xdclass.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.enums.*;
import net.xdclass.exception.BizException;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.model.CouponDO;
import net.xdclass.mapper.CouponMapper;
import net.xdclass.model.CouponRecordDO;
import net.xdclass.model.LoginUser;
import net.xdclass.request.NewUserCouponRequest;
import net.xdclass.service.CouponRecordService;
import net.xdclass.service.CouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.xdclass.until.CommonUtil;
import net.xdclass.until.JsonData;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 二当家小D
 * @since 2022-02-08
 */
@Slf4j
@Service
public class CouponServiceImpl extends ServiceImpl<CouponMapper, CouponDO> implements CouponService {

    @Autowired
    private CouponRecordService couponRecordService;

    @Autowired
    private RedissonClient redissonClient;

    @Cacheable(value = {"coupon"},key = "#root.methodName + #page+'_'+#size")
    @Override
    public Map<String, Object> pageCouponActivity(int page, int size) {
        Page<CouponDO> doPage = new Page<>(page, size);
        Page<CouponDO> couponDOPage = baseMapper.selectPage(doPage, new QueryWrapper<CouponDO>()
                .eq("publish", CouponPublisEnum.PUBLISH)
                .eq("category", CouponCategoryEnum.PROMOTION)
                .orderByDesc("create_time"));

        Map<String, Object> stringObjectMap = CommonUtil.beanProcess(couponDOPage, CouponDO.class);

        return stringObjectMap;
    }

//    String uuid = CommonUtil.generateUUID();
//    String lockKey = "lock:coupon:"+couponId;
//    Boolean nativeLock=redisTemplate.opsForValue().setIfAbsent(lockKey,uuid,Duration.ofSeconds(30));
//      if(nativeLock){
//        //加锁成功
//        log.info("加锁：{}",nativeLock);
//        try {
//            //执行业务  TODO
//        }finally {
//            String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
//
//            Integer result = redisTemplate.execute(new DefaultRedisScript<>(script, Integer.class), Arrays.asList(lockKey), uuid);
//            log.info("解锁：{}",result);
//        }
//
//    }else {
//        //加锁失败，睡眠100毫秒，自旋重试
//        try {
//            TimeUnit.MILLISECONDS.sleep(100L);
//        } catch (InterruptedException e) { }
//        return addCoupon( couponId, couponCategory);
//    }
//    //原生分布式锁 结束

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData addCoupon(long couponId, CouponCategoryEnum promotion) {
        LoginUser loginUser = LoginInterceptor.loginUserThreadLocal.get();

        log.info("领卷接口加锁成功:{}", Thread.currentThread().getId());
        try {
            CouponDO couponDO = baseMapper.selectOne(new QueryWrapper<CouponDO>()
                    .eq("id", couponId)
                    .eq("category", promotion.name())
                    .eq("publish", CouponPublisEnum.PUBLISH));
            if (couponDO == null) {
                throw new BizException(BizCodeEnum.COUPON_NO_EXITS);
            }
            this.checkCoupon(couponDO, loginUser.getId());

            CouponRecordDO couponRecordDO = new CouponRecordDO();
            BeanUtils.copyProperties(couponDO, couponRecordDO);
            couponRecordDO.setCreateTime(new Date());
            couponRecordDO.setUserId(loginUser.getId());
            couponRecordDO.setUserName(loginUser.getName());
            couponRecordDO.setUseState(CouponStateEnum.NEW.name());
            couponRecordDO.setCouponId(couponId);
            couponRecordDO.setId(null);

            int rows = baseMapper.reduceStock(couponId);
            if (rows == 1) {
                couponRecordService.getBaseMapper().insert(couponRecordDO);
            } else {
                log.warn("发放优惠券失效:{},用户:{}", couponDO, loginUser);
                throw new BizException(BizCodeEnum.COUPON_NO_STOCK);
            }
        } catch (Exception ex) {
            log.warn("发放优惠券失败ex:{},用户:{}", ex, loginUser);
        }
        return JsonData.buildSuccess();
    }

    private void checkCoupon(CouponDO couponDO, Long userId) {
        if (couponDO.getStock() <= 0) {
            throw new BizException(BizCodeEnum.COUPON_NO_STOCK);
        }
        if (!couponDO.getPublish().equals(CouponPublisEnum.PUBLISH.name())) {
            throw new BizException(BizCodeEnum.COUPON_GET_FAIL);
        }
        long timestamp = CommonUtil.getCurrentTimestamp();
        long startTime = couponDO.getStartTime().getTime();
        long endTime = couponDO.getEndTime().getTime();
        if (timestamp < startTime || timestamp > endTime) {
            throw new BizException(BizCodeEnum.COUPON_OUT_OF_TIME);
        }
        BaseMapper<CouponRecordDO> recordDOBaseMapper = couponRecordService.getBaseMapper();

        Integer recordCount = recordDOBaseMapper.selectCount(new QueryWrapper<CouponRecordDO>()
                .eq("coupon_id", couponDO.getId())
                .eq("user_id", userId));
        if (recordCount >= couponDO.getUserLimit()) {
            throw new BizException(BizCodeEnum.COUPON_OUT_OF_LIMIT);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData initNewUserCoupon(NewUserCouponRequest newUserCouponRequest) {
        LoginUser loginUser = new LoginUser();
        loginUser.setId(newUserCouponRequest.getUserId());
        loginUser.setName(newUserCouponRequest.getName());
        LoginInterceptor.loginUserThreadLocal.set(loginUser);

        List<CouponDO> category = baseMapper.selectList(new QueryWrapper<CouponDO>()
                .eq("category", CouponCategoryEnum.NEW_USER.name()));
        for (CouponDO couponDO : category) {
            this.addCoupon(couponDO.getId(), CouponCategoryEnum.NEW_USER);
        }
        return JsonData.buildSuccess();
    }
}
